#include <func.h>


int main()
{
    //1. 创建监听的套接字
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0) {
        perror("socket");
        return EXIT_FAILURE;
    }
    printf(">> listenfd: %d\n", listenfd);

    //设置套接字属性:网络地址可以重用
    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8000);
    //serveraddr.sin_addr.s_addr = inet_addr("192.168.30.129");
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//代表本机地址

    //2. 绑定网络地址
    int ret = bind(listenfd, (const struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret < 0) {
        perror("bind");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //3. 监听新连接的到来
    ret = listen(listenfd, 12800);
    if(ret < 0) {
        perror("listen");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //4. 使用select来监听listenfd和netfd（已经建立好连接的文件描述符）
    
    fd_set readset, monitorset;
    FD_ZERO(&readset);
    FD_ZERO(&monitorset);
    FD_SET(listenfd, &monitorset);
    int maxfd = listenfd;

    //事件循环，不断的对事件进行监听
    while(1) {
        printf("before select\n");
        FD_ZERO(&readset);
        readset = monitorset;//拷贝要监听的文件描述符
        int nready = select(maxfd + 1, &readset, NULL, NULL, NULL);
        printf(">> nready: %d\n", nready);

        if(FD_ISSET(listenfd, &readset)) {
           //5.1 有新连接过来
           struct sockaddr_in clientaddr;
           socklen_t len = sizeof(clientaddr);
           int netfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
           if(netfd < 0) {
               perror("accept");
           }
           //5.2 新连接信息的打印
           printf("conn %d tcp %s:%d-->%s:%d has connected.\n",
                           netfd, 
                           inet_ntoa(serveraddr.sin_addr),
                           ntohs(serveraddr.sin_port),
                           inet_ntoa(clientaddr.sin_addr),
                           ntohs(clientaddr.sin_port));
           //5.3 添加对netfd监听
           FD_SET(netfd, &monitorset);
           //5.4 更新maxfd的值
           if(maxfd < netfd) {
               maxfd = netfd;
           }
        }

        //6. 针对于已经建立好的连接的处理
        //由于数据结构是位图，只能遍历
        for(int i = 0; i < maxfd + 1; ++i) {
            if(FD_ISSET(i, &readset) && i != listenfd) {
                //6.1 读取数据
                char buff[1024] = {0};
                int ret = recv(i, buff, sizeof(buff), 0);
                if(0 == ret) {
                    //6.2 连接断开了
                    FD_CLR(i, &monitorset);
                    close(i);
                    printf("conn %d has closed.\n", i);
                    continue;
                }
                printf("recv %d bytes, content: %s.\n", ret, buff);
                //6.3 进行回显
                ret = send(i, buff, strlen(buff), 0);
                printf("send %d bytes.\n", ret);
            }
        }

    }

    close(listenfd);
    return 0;
}

